/*
 * Copyright (C) 2010 Ryan Morton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.games.rm.patternpuzzler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.view.Window;
import android.view.WindowManager;

public class Utilities
{
	public static final int MAIN_MENU_DELAY = 1000;//time in ms
	public static final int FINISH_ACTIVITY = 5122;
	public static final int GAME_TYPE_4_BUTTON = 1;
	public static final int GAME_TYPE_5_BUTTON = 2;//must be a higher value compared to GAME_TYPE_4_BUTTON
	public static final String VERSION_KEY = "version";
	public static final String PACKAGE_NAME = "com.games.rm.patternpuzzler";
	
	//gamesave key strings
	public static final int SAVE_GAME_VERSION = 2;
	public static final String SAVE_GAME_PREFERENCES = "save_game";
	public static final String GAME_TYPE_KEY = "gameType";
	public static final String PATTERN_KEY = "thePattern";
	public static final String LEVEL_COMPLETE_KEY = "isLevelComplete";
	
	//settings key strings
	public static final int SETTINGS_VERSION = 2;
	public static final String SETTINGS_PREFERENCES = "app_settings";
	public static final String DEFAULT_GAME_TYPE_KEY = GAME_TYPE_KEY;
	public static final String SOUND_FX_OPTION_KEY = "soundFx";
	public static final String SPEEDUP_KEY = "speedUp";
	
	//high scores key strings
	public static final int HIGH_SCORES_VERSION = 2;
	public static final String HIGH_SCORES_PREFERENCES = "high_scores";
	public static final String HIGH_SCORE_1_KEY = "high_score_1";
	public static final String HIGH_SCORE_2_KEY = "high_score_2";
	public static final String HIGH_SCORE_3_KEY = "high_score_3";
	public static final String HIGH_SCORE_4_KEY = "high_score_4";
	public static final String HIGH_SCORE_5_KEY = "high_score_5";
	public static final HashSet<String> HIGH_SCORES_KEY_SET = new HashSet<String>(Arrays.asList(HIGH_SCORE_1_KEY,HIGH_SCORE_2_KEY,HIGH_SCORE_3_KEY,
			HIGH_SCORE_4_KEY,HIGH_SCORE_5_KEY));
	
	/***
	 * Sets up the window properties for the passed Activity
	 * (e.g. fullscreen, no title bar, lock the orientation to portrait, etc.)
	 * @param a Activity in need of proper setup
	 */
	public static void setupActivityWindow(Activity a)
	{
		//Set app to display in fullscreen mode
		a.requestWindowFeature(Window.FEATURE_NO_TITLE);
		a.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 
        //forces device to render in landscape mode
        a.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
	}
	
	/***
	 * Takes the passed bitmap and returns a scaled up/down bitmap based on a target width
	 * @param image the bitmap
	 * @param targetWidth width desired for the output bitmap (if 0, height will be given priority)
	 * @param targetHeight height desired for the output bitmap (if 0, width will be given priority)
	 * @return a scaled image (with the correct aspect ratio if explicitly requested), or null if 
	 * both targetWidth and targetHeight are 0. For example, if 200 is passed for targetWidth and 0 for
	 * targetHeight, then the image will be scaled to 200 in width and some # Y in height, where Y=
	 * the aspect ratio appropriate value for this particular image. 
	 */
	public static Bitmap getScaledBitmap(Bitmap image, int targetWidth, int targetHeight)
	{
		if((targetHeight!=0 && targetWidth==0) || (targetHeight==0 && targetWidth!=0))
		{
			final float ar = (float)image.getWidth()/(float)image.getHeight();
			int newWidth = 0;
			int newHeight = 0;
			if(targetWidth == 0)
			{
				//scale height and keep A.R.
				newHeight = targetHeight;
				newWidth = (int)(ar*((float)targetHeight));
			}
			else if(targetHeight==0)
			{
				//scale width and keep A.R.
				newWidth = targetWidth;
				newHeight = (int)(((float)targetWidth)/ar);
			}
			else
			{
				// scale based on the exact targetWidth and targetHeight passed
				newWidth = targetWidth;
				newHeight = targetHeight;
			}
			
			return Bitmap.createScaledBitmap( image, newWidth, newHeight, true);
		}
		return null;
	}
	
	
	/***************************BELOW HANDLES SAVING INFO TO THE DEVICE*****************/
	/***
	 * @param c the calling Activity or Context
	 * @return true if a savegame exists, false otherwise
	 */
	public static boolean savegameExists(Context c)
	{
		return !c.getSharedPreferences(Utilities.SAVE_GAME_PREFERENCES, Activity.MODE_PRIVATE).getAll().isEmpty();
	}
		
	/***
	 * @param c the calling Activity or Context
	 * @param gameType an int representing the game type (see Utilities.GAME_TYPE_*)
	 * @param thePattern the interger pattern of button presses in an ArrayList
	 * @param timeRemaining the time (in sec.) remaining if gameType==Utilities.GAME_TYPE_5_BUTTON_WITH_TIMER. This should
	 * be null for all other game types.
	 * @param levelComplete set this to true if the level was completed when this was called
	 */	
	public static boolean saveCurrentGame(Context c, int gameType, ArrayList<Integer> thePattern, boolean levelComplete)
	{
		SharedPreferences mySaveGame  = c.getSharedPreferences(Utilities.SAVE_GAME_PREFERENCES, Activity.MODE_PRIVATE);
		if(!mySaveGame.getAll().isEmpty())
		{
			//clear out the old save game if present
			Utilities.deleteSavegame(c);
		}
		
		//create an editor
		Editor editor = mySaveGame.edit();
		
		//save game type
		switch(gameType)
		{
			case Utilities.GAME_TYPE_4_BUTTON:
			{
				editor.putInt(Utilities.GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON);
				break;
			}
			case Utilities.GAME_TYPE_5_BUTTON:
			{
				editor.putInt(Utilities.GAME_TYPE_KEY, Utilities.GAME_TYPE_5_BUTTON);
				break;
			}
			default:
			{
				editor.putInt(Utilities.GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON);
				break;
			}
		}
		
		//save the other values
		editor.putInt(VERSION_KEY, SAVE_GAME_VERSION);
		editor.putBoolean(Utilities.LEVEL_COMPLETE_KEY, levelComplete);
		editor.putString(Utilities.PATTERN_KEY, Utilities.convertIntListToString(thePattern));
		return editor.commit();
	}

	/***
	 * 
	 * @param c the calling Context or Activity
	 * @return a new Savegame object with the stored savegame preferences. Returns null if there is no
	 * savegame present or if there is some critical error with the important data in the savegame.
	 */
	public static Savegame loadSavegame(Context c)
	{
		SharedPreferences mySaveGame  = c.getSharedPreferences(Utilities.SAVE_GAME_PREFERENCES, Activity.MODE_PRIVATE);
		if(!mySaveGame.getAll().isEmpty())
		{			
			if(mySaveGame.getInt(GAME_TYPE_KEY, 0)!=0)
			{
				return new Savegame(mySaveGame.getInt(Utilities.GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON),
						mySaveGame.getString(Utilities.PATTERN_KEY, "1"), mySaveGame.getBoolean(LEVEL_COMPLETE_KEY, false));
			}
			else
			{
				//delete the stored savegame data... it might be corrupted
				Utilities.deleteSavegame(c);
				return null;
			}
		}
		//else
		return null;
	}
		
	/***
	 * Deletes all stored values for the savegame. 
	 * @param c the calling Context or Activity
	 * @return 
	 */
	public static boolean deleteSavegame(Context c)
	{
		return c.getSharedPreferences(Utilities.SAVE_GAME_PREFERENCES, Activity.MODE_PRIVATE).edit().clear().commit();
	}
	
	/***
	 * @param c the calling Activity or Context
	 * @return true if settings exists, false otherwise
	 */
	public static boolean settingsExists(Context c)
	{
		return !c.getSharedPreferences(Utilities.SETTINGS_PREFERENCES, Activity.MODE_PRIVATE).getAll().isEmpty();
	}
	
	/***
	 * 
	 * @param c the calling Context or Activity
	 * @param soundFxOn true if sound effects are to be enabled
	 * @param defaultGameType either Utilities.GAME_TYPE_4_BUTTON or Utilities.GAME_TYPE_5_Button
	 * @return
	 */
	public static boolean saveSettings(Context c, boolean soundFxOn, int defaultGameType, boolean speedIncreases)
	{
		Editor e = c.getSharedPreferences(Utilities.SETTINGS_PREFERENCES, Activity.MODE_PRIVATE).edit();
		
		//clear the settings first
		e.clear().commit();
		
		e.putInt(VERSION_KEY, SETTINGS_VERSION);
		e.putBoolean(SOUND_FX_OPTION_KEY, soundFxOn);
		if(defaultGameType == Utilities.GAME_TYPE_4_BUTTON)
		{
			e.putInt(DEFAULT_GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON);
		}
		else if(defaultGameType == Utilities.GAME_TYPE_5_BUTTON)
		{
			e.putInt(DEFAULT_GAME_TYPE_KEY, Utilities.GAME_TYPE_5_BUTTON);
		}
		else
		{
			e.putInt(DEFAULT_GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON);
		}
		
		e.putBoolean(SPEEDUP_KEY, speedIncreases);
		e.putInt(VERSION_KEY, SETTINGS_VERSION);
		
		return e.commit();
	}
	
	/***
	 * 
	 * @param c the calling Context or Activity
	 * @return a GameSettings object holding the saved game settings
	 */
	public static GameSettings loadSettings(Context c)
	{
		SharedPreferences mySettings  = c.getSharedPreferences(Utilities.SETTINGS_PREFERENCES, Activity.MODE_PRIVATE);
		if(mySettings.getAll().isEmpty())
		{
			Utilities.resetSettingsToDefault(c);
			return Utilities.loadSettings(c);
		}
				
		return new GameSettings(mySettings.getBoolean(SOUND_FX_OPTION_KEY, true), 
				mySettings.getInt(DEFAULT_GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON), mySettings.getBoolean(SPEEDUP_KEY, false));
	}
	
	/***
	 * Resets the stored game settings to default values (e.g. sound effects on, default game type to 4-button, etc...)
	 * @param c the calling Context or Activity
	 * @return
	 */
	public static boolean resetSettingsToDefault(Context c)
	{
		Editor e = c.getSharedPreferences(Utilities.SETTINGS_PREFERENCES, Activity.MODE_PRIVATE).edit();
		
		//clear the settings first
		e.clear().commit();
		
		e.putBoolean(SOUND_FX_OPTION_KEY, true);
		e.putInt(DEFAULT_GAME_TYPE_KEY, Utilities.GAME_TYPE_4_BUTTON);
		e.putBoolean(SPEEDUP_KEY, false);
		
		return e.commit();
	}
	
	/***
	 * 
	 * @param c the calling Context or Activity
	 * @return a sorted ArrayList of HighScore objects
	 */
	public static ArrayList<HighScore> loadHighScores(Context c)
	{
		SharedPreferences myScores  = c.getSharedPreferences(Utilities.HIGH_SCORES_PREFERENCES, Activity.MODE_PRIVATE);
		if(!myScores.getAll().isEmpty())
		{
			String[] splitString;
			ArrayList<HighScore> result = new ArrayList<HighScore>();
			for(String key : Utilities.HIGH_SCORES_KEY_SET)
			{
				splitString = myScores.getString(key, "").split(";");
				if(splitString.length==3)
				{
					//high score found
					try
					{
						result.add(new HighScore(splitString[0], Integer.parseInt(splitString[1]), Integer.parseInt(splitString[2])));
					}
					catch(Exception e)
					{
						continue;
					}
				}
			}
			Collections.sort(result);
			return result;
		}
		//else
		return new ArrayList<HighScore>();
	}
	
	/***
	 * Attempts to add a finished game to the the high scores list.
	 * @param c the calling Context or Activity
	 * @param gameType the game type to save with the high score
	 * @param currentLevel the level to save with the high score
	 */
	public static boolean addHighScore(Context c, int gameType, int currentLevel)
	{
		Editor e = c.getSharedPreferences(Utilities.HIGH_SCORES_PREFERENCES, Activity.MODE_PRIVATE).edit();
		ArrayList<HighScore> myScores = Utilities.loadHighScores(c);
		
		final Calendar cal = Calendar.getInstance();
		
		myScores.add(new HighScore(String.format("%tD", cal), gameType, currentLevel));
		Collections.sort(myScores);//need to reorder the list because we will only save the top 5 scores
		//clear sored high scores
		Utilities.clearHighScores(c);
		
		//save the top 5 highscores
		for(int i=0; i<myScores.size(); i++)
		{
			if(i<5)
			{
				switch(i)
				{
					case 0:
					{
						e.putString(HIGH_SCORE_1_KEY, myScores.get(i).toString());
						break;
					}
					case 1:
					{
						e.putString(HIGH_SCORE_2_KEY, myScores.get(i).toString());
						break;
					}
					case 2:
					{
						e.putString(HIGH_SCORE_3_KEY, myScores.get(i).toString());
						break;
					}
					case 3:
					{
						e.putString(HIGH_SCORE_4_KEY, myScores.get(i).toString());
						break;
					}
					case 4:
					{
						e.putString(HIGH_SCORE_5_KEY, myScores.get(i).toString());
						break;
					}
					default:
					{
						e.putString(HIGH_SCORE_5_KEY, myScores.get(i).toString());
						break;
					}
				}
			}
			else
			{
				break;
			}
		}
		
		return e.commit();
	}
	
	/***
	 * Clears out all saved high scores.
	 * @param c the calling Context or Activity
	 * @return
	 */
	public static boolean clearHighScores(Context c)
	{
		return c.getSharedPreferences(Utilities.HIGH_SCORES_PREFERENCES, Activity.MODE_PRIVATE).edit().clear().commit();
	}
	
	/***
	 * Converts the passed ArrayList<Integer> to a flat ; delimited String
	 * @param list
	 * @return a flat ; delimited String
	 */
	public static String convertIntListToString(ArrayList<Integer> list)
	{
		String result = "";
		for(int i=0;i<list.size();i++)
		{
			if(i==(list.size()-1))
			{
				//the last value doesn't need a ";" after it
				result+=list.get(i);
			}
			else 
			{
				result+=list.get(i)+";";
			}
		}
		
		return result;
	}
	/***************************************************************/
}
